package com.chatplus.application.elasticsearch.service;


import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregation;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.core.BulkResponse;
import co.elastic.clients.elasticsearch.core.IndexResponse;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.json.JsonData;
import com.fasterxml.jackson.databind.JsonNode;
import com.chatplus.application.elasticsearch.model.vo.IndexDataToSave;

import java.io.IOException;
import java.util.*;


/**
 * EsService例子
 */
//@RunWith(SpringRunner.class)
//@SpringBootTest(classes = MerchantApplication.class)
public class EsServiceExample {
    //@Autowired
    private EsService<EsDemo> esService;
    static String index = "demo";

    /**
     * 数量查询例子
     */
//    @Test
    public void countTest() throws IOException {

        Query query = new Query.Builder()
            .bool(boolQuery ->
                boolQuery
                    // 在同一个 boolQuery 中 must 会将 should 覆盖
                    .must(must -> must.range(
                        e -> e.field("age").gte(JsonData.of("21")).lte(JsonData.of("25"))
                    ))

                    .mustNot(mustNot -> mustNot.term(
                        e -> e.field("name").value(value -> value.stringValue("lisi1"))
                    ))
                    .should(must -> must.term(
                        e -> e.field("name").value(value -> value.stringValue("lisi2"))
                    ))
            )
            .build();
        long count = esService.countDocument(index, null);
        System.out.println(count);
    }

    //    @Test
    public void saveTest() throws Exception {
        EsDemo esDemo = new EsDemo();
        esDemo.setAge("1111");
        esDemo.setName("Angus");
        IndexResponse noId = esService.save(index, esDemo);
        System.out.println(noId.index());
        System.out.println(noId.id());
        System.out.println(noId.version());

        IndexResponse haveId = esService.save(index, "1234455", esDemo);
        System.out.println(haveId.index());
        System.out.println(haveId.id());
        System.out.println(haveId.version());
    }

    //@Test
    public void saveBatchTest() throws Exception {
        List<IndexDataToSave> dataList = new ArrayList<>();
        EsDemo one = new EsDemo();
        one.setAge("1111");
        one.setName("Angus");
        dataList.add(new IndexDataToSave("1111", one));
        EsDemo two = new EsDemo();
        two.setAge("2222");
        two.setName("Angus");
        dataList.add(new IndexDataToSave("2222", two));
        //同步保存数据，会堵塞
        BulkResponse bulkResponse = esService.saveBatch(index, dataList);
        //异步保存数据，不会堵塞
        esService.saveBatch(index, dataList);

        System.out.println(bulkResponse.errors());
    }

    //@Test
    public void indexTest() throws Exception {
        System.out.println(esService.isIndexExisted(index));
        System.out.println(esService.deleteIndexIfExisted("xxxxxxx"));
    }

    //@Test
    public void rolloverTest() throws Exception {
        //参考wiki
        //https://www.tapd.cn/47500486/markdown_wikis/show/#1147500486001001944
        System.out.println(esService.rolloverNewIndex("demo", false));
    }

    // @Test
    public void queryTest() throws IOException {
        //简单查询

        //term
        Query termQ = Query.of(query -> query.term(
            termQuery -> termQuery.field("name.keyword").value(value -> value.stringValue("Angus"))
        ));
        //match
        Query matchQ = Query.of(query -> query.match(
            matchQuery -> matchQuery.field("name").query(value -> value.stringValue("Angus"))
        ));
        //range, 数字范围
        Query rangeNumQ = Query.of(query -> query.range(
            rangeQuery -> rangeQuery.field("age").gte(JsonData.of("21")).lte(JsonData.of("25"))
        ));
        //range, 时间范围
        Query rangeTimeQ = Query.of(query -> query.range(
            rangeQuery -> rangeQuery.timeZone("").field("age").gte(JsonData.of("15")).lte(JsonData.of("25")).timeZone("Asia/Shanghai")
        ));
        //exists 查询
        Query existsQ = Query.of(query -> query.exists(
            existsQuery -> existsQuery.field("name").queryName("Angus")
        ));
        // geo 查询
//        Query geoQ = new Query.Builder()
//                .geoDistance( geoQuery -> geoQuery.field("geoipxx").location( loc -> loc.)
//                )
//                .build();
        //复杂查询,一般复杂查询就是用到了boolQuery查询的
        Query boolQ = Query.of(query -> query.bool(
            boolQuery -> boolQuery
                // 在同一个 boolQuery 中 must 会将 should 覆盖
                .must(must -> must.range(
                    e -> e.field("agen").gte(JsonData.of("21")).lte(JsonData.of("25"))
                ))
                .must(must -> must.term(
                    e -> e.field("age").field("name").value(value -> value.stringValue("Angus"))
                ))
                .mustNot(mustNot -> mustNot.term(
                    e -> e.field("name").value(value -> value.stringValue("lisi1"))
                ))
                .should(must -> must.term(
                    e -> e.field("name").value(value -> value.stringValue("lisi2"))
                ))
        ));

        // 索引名称列表
        List<String> names = Arrays.asList("idx-a", "idx-b", "idx-c");

        // 可以把聚合内容放到Map，为字段“foo”和“bar”准备唯一数聚合
        Map<String, Aggregation> cardinalities = new HashMap<>();
        cardinalities.put("foo-count", Aggregation.of(a -> a.cardinality(c -> c.field("foo"))));
        cardinalities.put("bar-count", Aggregation.of(a -> a.cardinality(c -> c.field("bar"))));

        // 也可以单独设置一个聚合，一个计算“size”字段平均值的聚合
        final Aggregation avgSize = Aggregation.of(a -> a.avg(v -> v.field("size")));
        //dateHistogram 聚合
        final Aggregation timeAbg = Aggregation.of(a -> a.dateHistogram(
            myTime -> myTime.field("time").timeZone("Asia/Shanghai").interval(ti -> ti.time("1h"))
        ));

        SearchRequest searchRequest = SearchRequest.of(r -> r
            // Index list:
            // - 单个索引查询
            .index(index)
            // - 多个索引查询
            .index(names)
            //添加查询语句方式一
            .query(termQ)
            .query(boolQ)
            //添加查询语句方式二
            .query(
                myTerm -> myTerm.term(
                    termQuery -> termQuery.field("name.keyword").value(value -> value.stringValue("Angus"))
                )
            )
            // 数据排序顺序
            .sort(s -> s.field(f -> f.field("foo").order(SortOrder.Asc)))
            .sort(s -> s.field(f -> f.field("bar").order(SortOrder.Desc)))
            // 分页
            .from(0)
            .size(100)
            // 聚合列表:
            // - 添加现有所有条目
            .aggregations(cardinalities)
            // - 添加key key/value
            .aggregations("avg-size", avgSize)
            // - 也可以像以下一样聚合
            .aggregations("price-histogram",
                a -> a.histogram(h -> h.field("price")))
        );
        // 返回数据List
        List<EsDemo> list = esService.searchDocList(searchRequest, EsDemo.class);
        // 返回SearchResponse
        SearchResponse<JsonNode> searchResponse = esService.search(searchRequest);

    }

    public void deleteTest() throws IOException {
        Query termQ = new Query.Builder()
            .term(termQuery -> termQuery.field("name.keyword").value(value -> value.stringValue("Angus"))
            )
            .build();
        //根据ID删除
        esService.deleteDocumentById(index, "123445");
        //跟进查询条件删除
        esService.deleteDocumentByQuery(index, termQ, false);
    }

    //@Test
    public void getAggDataTest() throws IOException {
        // 也可以单独设置一个聚合，一个计算“size”字段平均值的聚合
        final Aggregation terms = Aggregation.of(a -> a.terms(
            termsA -> termsA.field("name.keyword")
        ));

        SearchRequest searchRequest = SearchRequest.of(r -> r
            // Index list:
            // - 单个索引查询
            .index(index)
            // 聚合列表:
            // - 添加key key/value
            .aggregations("myterms", terms)
        );
        // 返回SearchResponse
        SearchResponse<JsonNode> searchResponse = esService.search(searchRequest);

        // 获取聚合数据列表
        Map<String, Aggregate> aggregations = searchResponse.aggregations();

        for (Map.Entry<String, Aggregate> entry : aggregations.entrySet()) {

            System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
            Aggregate termsAggregation = entry.getValue();
            System.out.println(termsAggregation.isSterms());
            termsAggregation.sterms().buckets().array().forEach(termsData -> {
                System.out.println("Key = " + termsData.key() + ", docCount = " + termsData.docCount());

            });

        }
    }
}

class EsDemo {
    String name;
    String age;
    String stream;
    String type;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public String getStream() {
        return stream;
    }

    public void setStream(String stream) {
        this.stream = stream;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }
}
